﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sio.Mdm.Server;
using Sio.Mdm.Types.Database;
using Sio.Mdm.BF.Administracija;
using Sio.Mdm.Entities;
using Sio.Mdm.Types;
using Sio.Mdm.BL.Base;
using System.Security.Cryptography;
using System.IO;

namespace Sio.Mdm.BL.Administracija
{
    [Service(Name = "AuthorizationService", DefaultInterface = typeof(IAvtorizacijaService))]
    public class AvtorizacijaService : BaseFirebird<UporabniskiRacun>, IAvtorizacijaService
    {
        public AvtorizacijaService()
            : base("IDM")
        {
        }

        private Session CheckLogin(string uporabniskoIme, string geslo, int organizacijaId, UporabniskiRacun user)
        {
            bool ok = false;

            var cryptedPass = PasswordHandling.Encrypt(geslo);
            if (user.UporabniskoIme.Equals(uporabniskoIme) && ((user.Geslo.Equals(geslo) || (user.Geslo.Equals(cryptedPass)))))
            {
                if (user.Geslo.Equals(geslo)) //v bazi je očitno še nekriptirano
                {
                    user.Geslo = cryptedPass;
                    user.Update();
                }

                var org = MakeSelect<Organizacija>(new Filter<Organizacija>().Where(r => r.OrganizacijaId == organizacijaId), null, false).First();

                var vloga = MakeSelect<Vloga>(new Filter<Vloga>().Where(r => r.IdmStevilka == user.IdmStevilka && r.OrganizacijaId == user.OrganizacijaId), null, false).First();

                var result = new Sio.Mdm.Types.Session(user.IdmStevilka, Guid.NewGuid(), uporabniskoIme, org.OrganizacijaId, user.Ime, user.Priimek, org.Naziv, vloga.VlogaId, vloga.TipVlogeId);
                return result;
            }
            return null;
        }

        public Result Login(string uporabniskoIme, string geslo, int organizacijaId, out Session session)
        {
            session = null;

            //Uporabnik je lahko navaden, ali pa supperAdmin. Če je superAdmin ima idm_stevilko 0 in ga obravnavam ločeno
            EntityCollection<UporabniskiRacun> userList = new EntityCollection<UporabniskiRacun>(
                new Filter<UporabniskiRacun>().Where(r => r.UporabniskoIme == uporabniskoIme && r.OrganizacijaId == organizacijaId && organizacijaId != 0));

            EntityCollection<UporabniskiRacun> superAdminList = new EntityCollection<UporabniskiRacun>(
                new Filter<UporabniskiRacun>().Where(r => r.UporabniskoIme == uporabniskoIme && r.OrganizacijaId == 0 && r.IdmStevilka == 0));



            if (userList.Count.Equals(1))
            {
                session = CheckLogin(uporabniskoIme, geslo, organizacijaId, userList.First());
            }
            else if (superAdminList.Count.Equals(1))
            {
                session = CheckLogin(uporabniskoIme, geslo, organizacijaId, superAdminList.First());
            }
            else
               return new Result(String.Format("Napaka pri prijavi. Uporabnika ni mogoče identificirati."));

            if (session == null)
                return new Result(String.Format("Napaka pri prijavi. Napačno geslo ali pa nimate pravic dostopa."));
            else
               return null;

            /*
                if (((userList == null) || (userList.Count != 1)) && (supeAdminList.Count != 1))
                    return new Result(String.Format("Napaka pri prijavi. Uporabnika ni mogoče identificirati."));
                else
                {
                    var user = userList.First();
                    if (user.UporabniskoIme != uporabniskoIme)
                        return new Result(String.Format("Napaka pri prijavi. Uporabnika ni mogoče enolično identificirati."));

                    //preverjanje gesla...
                    //najprej preverim če
                    var cryptedPass = PasswordHandling.Encrypt(geslo);
                    if (user.Geslo.Equals(geslo) || user.Geslo.Equals(cryptedPass))
                    {
                        //če v bazi še ni kriptirano geslo ga kriptiram in zapišem v bazo
                        if (user.Geslo.Equals(geslo))
                        {
                            user.Geslo = cryptedPass;
                            user.Update();
                        }

                        //

                        var org = MakeSelect<Organizacija>(new Filter<Organizacija>().Where(r => r.OrganizacijaId == organizacijaId), session, false).First();

                        var vloga = MakeSelect<Vloga>(new Filter<Vloga>().Where(r => r.IdmStevilka == user.IdmStevilka && r.OrganizacijaId == organizacijaId), session, false).First();
                        tipVlogeId = vloga.TipVlogeId;

                        session = new Sio.Mdm.Types.Session(user.IdmStevilka, Guid.NewGuid(), uporabniskoIme, organizacijaId, user.Ime, user.Priimek, org.Naziv, vloga.VlogaId);
                        return null;
                    }
                    else
                        return new Result(String.Format("Napaka pri prijavi. Napačno geslo."));
                }*/
        }

        public Result Logout(ref Session session)
        {
            session = Session.Empty;
            // TODO : Maybe do some log or something
            return null;
        }

        public Result EncryptPassword(ref string password)
        {
            password = PasswordHandling.Encrypt(password);
            return null;
        }

        public Result DecryptPassword(ref string password)
        {
            password = PasswordHandling.Decrypt(password);
            return null;
        }

        public Result SecurityTokens(Session session, ref IList<ZetoniUporabnika> tokens)
        {
            var zetoni = MakeSelect<ZetoniUporabnika>(new Filter<ZetoniUporabnika>(session.IdmStevilka, session.Organizacija), session, false);
            tokens = zetoni.ToList();
            return null;
        }

        internal static class PasswordHandling
        {
            static string initVector = "@1B2c3D4e5F6g7H8";
            static string passPhrase = "Pas5pr@se";
            static string saltValue = "s@1tValue";
            static string hashAlgorithm = "SHA1"; //can be "MD5" or "SHA1";
            
            public static string Encrypt(string plainText)
            {
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

                byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

                PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, 2);

                byte[] keyBytes = password.GetBytes(256 / 8);

                RijndaelManaged symmetricKey = new RijndaelManaged();
                symmetricKey.Mode = CipherMode.CBC;
                ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
                MemoryStream memoryStream = new MemoryStream();

                CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

                cryptoStream.FlushFinalBlock();
                byte[] cipherTextBytes = memoryStream.ToArray();
                memoryStream.Close();
                cryptoStream.Close();

                string cipherText = Convert.ToBase64String(cipherTextBytes);
                return cipherText;
            }

            public static string Decrypt(string cipherText)
            {
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
                byte[] cipherTextBytes;
                try
                {
                  cipherTextBytes = Convert.FromBase64String(cipherText);
                }
                catch
                {
                  return cipherText;
                }

                PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes, hashAlgorithm, 2);

                byte[] keyBytes = password.GetBytes(256 / 8);

                RijndaelManaged symmetricKey = new RijndaelManaged();
                symmetricKey.Mode = CipherMode.CBC;

                ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);

                MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

                CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                              decryptor,
                                                              CryptoStreamMode.Read);

                byte[] plainTextBytes = new byte[cipherTextBytes.Length];

                int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

                memoryStream.Close();
                cryptoStream.Close();

                string plainText = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                return plainText;
            }
        }
    }

}
